﻿using System;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using SpringGraphTwitterExample.Entities;
using System.IO;
using System.Xml.Linq;

namespace SpringGraphTwitterExample.Twitter
{
    public class TwitterClient
    {
        private const string URL_GET_USER = "/ProxyService/ProxyService.ashx?reqUrl=http://api.twitter.com/1/users/show.xml%3Fscreen_name%3D";

        private const string URL_GET_USER_BY_ID = "/ProxyService/ProxyService.ashx?reqUrl=http://api.twitter.com/1/users/show.xml%3Fuser_id%3D";

        private const string URL_GET_FRIENDS = "/ProxyService/ProxyService.ashx?reqUrl=http://api.twitter.com/1/friends/ids.xml%3Fuser_id%3D";

        public event EventHandler<GetUserAndFriendsEventArgs> GetUserAndFriendsCompleted;

        public void GetUserAndFriendsAsync(string screenName)
        {
            WebClient client = new WebClient();
            client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted_GetUser);
            client.OpenReadAsync(new Uri(URL_GET_USER + screenName, UriKind.Relative));
        }

        void  client_OpenReadCompleted_GetUser(object sender, OpenReadCompletedEventArgs e)
        {
            if (e.Error != null)
                MessageBox.Show("Error getting user");
            else
            {
                using (Stream s = e.Result)
                {
                    var user = GetUser(s);
                    var state = new GetUserAndFriendsEventState()
                    {
                        Users = new List<User>()
                        {
                            user
                        }
                    };

                    WebClient client = new WebClient();
                    client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted_GetUserFriends);
                    client.OpenReadAsync(new Uri(URL_GET_FRIENDS + user.Id, UriKind.Relative), state);
                }
            }
        }

        void client_OpenReadCompleted_GetUserFriends(object sender, OpenReadCompletedEventArgs e)
        {
            if (e.Error != null)
                MessageBox.Show("Error getting user friends");
            else
            {
                using (Stream s = e.Result)
                {
                    var ids = GetIds(s);
                    var state = e.UserState as GetUserAndFriendsEventState;
                    state.Users.First().FriendsIds = ids;
                    state.PendingIds = ids;

                    GetNextUser(state);
                }
            }
        }

        private void GetNextUser(GetUserAndFriendsEventState state)
        {
            var nextId = state.PendingIds.FirstOrDefault();

            if (nextId != null)
            {
                state.PendingIds.Remove(nextId);
                WebClient client = new WebClient();
                client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted_GetNextUser);
                client.OpenReadAsync(new Uri(URL_GET_USER_BY_ID + nextId, UriKind.Relative), state);
            }
            else
            {
                if (GetUserAndFriendsCompleted != null)
                    GetUserAndFriendsCompleted(this, new GetUserAndFriendsEventArgs()
                    {
                        Users = state.Users
                    });
            }
        }

        void client_OpenReadCompleted_GetNextUser(object sender, OpenReadCompletedEventArgs e)
        {
            if (e.Error != null)
                GetNextUser(e.UserState as GetUserAndFriendsEventState);
            else
            {
                using (Stream s = e.Result)
                {
                    var user = GetUser(s);
                    var state = e.UserState as GetUserAndFriendsEventState;
                    state.Users.Add(user);

                    WebClient client = new WebClient();
                    client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted_GetNextUserFriends);
                    client.OpenReadAsync(new Uri(URL_GET_FRIENDS + user.Id, UriKind.Relative), state);
                }
            }
        }

        void client_OpenReadCompleted_GetNextUserFriends(object sender, OpenReadCompletedEventArgs e)
        {
            if (e.Error != null)
                GetNextUser(e.UserState as GetUserAndFriendsEventState);
            else
            {
                using (Stream s = e.Result)
                {
                    var ids = GetIds(s);
                    var state = e.UserState as GetUserAndFriendsEventState;
                    state.Users.Last().FriendsIds = ids;

                    GetNextUser(state);
                }
            }
        }

        private List<string> GetIds(Stream s)
        {
            return (from id in XDocument.Load(s).Descendants("ids").First().Descendants("id")
                      select id.Value).ToList();
        }

        private User GetUser(Stream s)
        {
            return (from u in XDocument.Load(s).Descendants("user")
                       select new User()
                       {
                           Id = u.Element("id").Value,
                           Name = u.Element("name").Value,
                           ScreenName = u.Element("screen_name").Value,
                           ProfileImage = u.Element("profile_image_url").Value
                       }).First();
        }
    }

    public class GetUserAndFriendsEventArgs : EventArgs
    {
        public List<User> Users
        {
            get;
            set;
        }
    }

    public class GetUserAndFriendsEventState
    {
        public List<User> Users
        {
            get;
            set;
        }

        public List<string> PendingIds
        {
            get;
            set;
        }
    }
}
